#include <iostream>
#include <windows.h>
#include <sstream>
#include <string>
#include <fstream>
#include <stdio.h>
#include <vector>
#include <iterator>
#include "TcpThread.h"

using namespace std;

void TcpThread::run()
{
	memset(szbuffer, '\0', BUFFER_SIZE);
	//Fill in szbuffer from accepted request.
	if ((ibytesrecv = recv(cs, szbuffer, BUFFER_SIZE, 0)) == SOCKET_ERROR)
		throw "Receive error in server program\n";

	//Print reciept of successful message. 
	//cout << "This is message from client: " << szbuffer << endl;

	string text(szbuffer);
	if (text.find("operation:put") == 0)
	{
		int firstSplitterIndex = text.find_first_of("|");
		int secondSplitterIndex = firstSplitterIndex + 1 + text.substr(firstSplitterIndex + 1).find_first_of("|");
		int thirdSplitterIndex = secondSplitterIndex + 1 + text.substr(secondSplitterIndex + 1).find_first_of("|");
		int fourthSplitterIndex = thirdSplitterIndex + 1 + text.substr(thirdSplitterIndex + 1).find_first_of("|");
		string userName = text.substr(firstSplitterIndex + 10, secondSplitterIndex - firstSplitterIndex - 10);
		string fileName = text.substr(secondSplitterIndex + 10, thirdSplitterIndex - secondSplitterIndex - 10);
		string countStr = text.substr(thirdSplitterIndex + 7, fourthSplitterIndex - thirdSplitterIndex - 7 );
		string binaryStr = text.substr(fourthSplitterIndex + 8);
		int count = stringToInt(countStr);
		bool isBinaryFile = (binaryStr == "1") ? true : false;
		cout << "User \"" + userName + "\" requested to put file " + fileName << endl;
		ofstream ofs;
		if (isBinaryFile)
		{
			ofs = ofstream(fileName, ofstream::binary);
		}
		else
		{
			ofs = ofstream(fileName, std::ofstream::out);
		}
		if (isBinaryFile)
		{
			while (count > 0)
			{
				memset(szbuffer, '\0', BUFFER_SIZE);
				ibytesrecv = 0;
				if ((ibytesrecv = recv(cs, szbuffer, BUFFER_SIZE, 0)) == SOCKET_ERROR)
					throw "Receive failed\n";
				//cout << "text received:" << szbuffer << endl;
				ofs.write(szbuffer, BUFFER_SIZE);
				ofs.flush();
				count--;
			}

		}
		else
		{
			while (count > 0)
			{
				memset(szbuffer, '\0', BUFFER_SIZE);
				ibytesrecv = 0;
				if ((ibytesrecv = recv(cs, szbuffer, BUFFER_SIZE, 0)) == SOCKET_ERROR)
					throw "Receive failed\n";
				//cout << "text received:" << szbuffer << endl;
				szbuffer[BUFFER_SIZE - 1] = '\0';
				ofs << szbuffer;
				ofs.flush();
				count--;
			}
		}
		cout << "file completely received" << endl;
		ofs.close();
	}
	else if (text.find("operation:get") == 0)
	{
		int firstSplitterIndex = text.find_first_of("|");
		int secondSplitterIndex = firstSplitterIndex + 1 + text.substr(firstSplitterIndex + 1).find_first_of("|");
		string userName = text.substr(firstSplitterIndex + 10, secondSplitterIndex - firstSplitterIndex - 10);
		string fileName = text.substr(secondSplitterIndex + 10);
		cout << "User \"" + userName + "\" requested to get file " + fileName << endl;
		if (fileExist(fileName))
		{
			if (isBinaryFile(fileName))
			{
				ifstream fin = ifstream(fileName, ifstream::binary); //opening an input stream for file
				int fileSize = getFileSize(fileName);
				int count = fileSize % BUFFER_SIZE == 0 ? fileSize / BUFFER_SIZE : fileSize / BUFFER_SIZE + 1;
				string text = "count:" + to_string(count) + "|binary:1";
				const char * reply = text.c_str();
				ibufferlen = strlen(reply);
				if ((ibytessent = send(cs, reply, ibufferlen, 0)) == SOCKET_ERROR)
					throw "error in send in server program\n";
				/*
				else cout << "Echo message:" << szbuffer << endl;
				*/
				while (count > 0)
				{
					char reply[BUFFER_SIZE];
					memset(reply, '\0', BUFFER_SIZE);
					fin.read(reply, BUFFER_SIZE);
					if ((ibytessent = send(cs, reply, BUFFER_SIZE, 0)) == SOCKET_ERROR)
						throw "error in send in server program\n";
					/*
					else cout << "Echo message:" << reply << endl;
					*/
					count--;
				}
				cout << "file completely transfered!" << endl;
				fin.close();
			}
			else
			{
				ifstream fin = ifstream(fileName);
				int count = getTextFileCount(fileName);
				string text = "count:" + to_string(count) + "|binary:0";
				const char * reply = text.c_str();
				ibufferlen = strlen(reply);
				if ((ibytessent = send(cs, reply, ibufferlen, 0)) == SOCKET_ERROR)
					throw "error in send in server program\n";
				/*
				else cout << "Echo message:" << szbuffer << endl;
				*/
				while (count > 0)
				{
					char reply[BUFFER_SIZE];
					memset(reply, '\0', BUFFER_SIZE);
					int position = 0;
					while (!fin.eof())
					{
						char c = fin.get(); //reading one character from file to array
						if (c != EOF)
						{
							reply[position] = c;
							position++;
							if (position == BUFFER_SIZE - 1)	//full
							{
								reply[position] = '\0';
								break;
							}
						}
					}
					if (position != BUFFER_SIZE - 1)
					{
						reply[position] = '\0';
					}
					ibufferlen = strlen(reply);
					if ((ibytessent = send(cs, reply, ibufferlen + 1, 0)) == SOCKET_ERROR)
						throw "error in send in server program\n";
					/*
					else cout << "Echo message:" << reply << endl;
					*/
					count--;
				}
				cout << "file completely transfered!" << endl;
				fin.close();
			}
		}
		else
		{
			string text = "can not find file " + fileName;
			const char * reply = text.c_str();
			ibufferlen = strlen(reply);
			if ((ibytessent = send(cs, reply, ibufferlen, 0)) == SOCKET_ERROR)
				throw "error in send in server program\n";
			/*
			else cout << "Echo message:" << szbuffer << endl;
			*/
		}
	}
	else if (text.find("operation:list") == 0)
	{
		int firstSplitterIndex = text.find_first_of("|");
		string userName = text.substr(firstSplitterIndex + 10);
		cout << "User \"" + userName + "\" requested to get file list in server" << endl;
		TCHAR infoBuf[200];
		if (!GetCurrentDirectory(200, infoBuf)){
			printf("GetCurrentDirectory() failed!\n");
		}
		string curDir = infoBuf;
		//printf("Your current directory is: %s\n", curDir.c_str());
		vector<string> vec = ListDirectoryContents(curDir.c_str());
		string listResult = "";
		for (vector<string>::const_iterator ite = vec.begin(); ite != vec.end(); ++ite)
		{
			listResult += *ite;
			listResult += "\n";
		}

		int count = listResult.length() % (BUFFER_SIZE - 1) == 0 ? listResult.length() / (BUFFER_SIZE - 1) : (listResult.length() / (BUFFER_SIZE - 1) + 1);
		int totalCount = count;
		string text = "count:" + to_string(count);
		const char * reply = text.c_str();

		//Send to Client the received message (echo it back).
		ibufferlen = strlen(reply);
		if ((ibytessent = send(cs, reply, ibufferlen, 0)) == SOCKET_ERROR)
			throw "error in send in server program\n";
		/*
		else cout << "Echo message:" << szbuffer << endl;
		*/
		while (count > 0)
		{
			char * reply = new char[BUFFER_SIZE];
			memset(reply, '\0', BUFFER_SIZE);
			int startIndex = (BUFFER_SIZE - 1) * (totalCount - count);
			string subString = listResult.substr(startIndex, BUFFER_SIZE - 1);
			int i = 0;
			for (; i < subString.length(); i++)
			{
				reply[i] = subString.at(i);
			}
			count--;
			reply[BUFFER_SIZE - 1] = '\0';

			//Send to Client the received message (echo it back).
			ibufferlen = strlen(reply);
			if ((ibytessent = send(cs, reply, ibufferlen+1, 0)) == SOCKET_ERROR)
				throw "error in send in server program\n";
			/*
			else cout << "Echo message:" << reply << endl;
			*/
			delete reply;
		}
	}
	else if (text.find("operation:delete") == 0)
	{
		int firstSplitterIndex = text.find_first_of("|");
		int secondSplitterIndex = firstSplitterIndex + 1 + text.substr(firstSplitterIndex + 1).find_first_of("|");
		string userName = text.substr(firstSplitterIndex + 10, secondSplitterIndex - firstSplitterIndex - 10);
		string fileName = text.substr(secondSplitterIndex + 10);
		cout << "User \"" + userName + "\" requested to delete file " + fileName << endl;
		string text;
		if (fileExist(fileName))
		{
			if (remove(fileName.c_str()) != 0)
			{
				text = "delete file " + fileName + " failed";
			}
			else
			{
				text = "delete file " + fileName + " succeeded";
			}
		}
		else
		{
			text = "can not find file " + fileName;
		}
		cout << text << endl;
		const char * reply = text.c_str();
		//Send to Client the received message (echo it back).
		ibufferlen = strlen(reply);
		if ((ibytessent = send(cs, reply, ibufferlen, 0)) == SOCKET_ERROR)
			throw "error in send in server program\n";
		/*
		else cout << "Echo message:" << szbuffer << endl;
		*/
	}
	else if (text.find("operation:quit") == 0)
	{
		int firstSplitterIndex = text.find_first_of("|");
		string userName = text.substr(firstSplitterIndex + 10);
		cout << "User \"" + userName + "\" requested to quit" << endl;
	}
	else
	{
		int firstSplitterIndex = text.find_first_of("|");
		string userName = text.substr(firstSplitterIndex + 10);
		cout << "User \"" + userName + "\" did invalid operation" << endl;
	}
	closesocket(cs);
}

int TcpThread::stringToInt(string s)
{
	istringstream stringBuffer(s);
	int value;
	stringBuffer >> value;
	return value;
}


int TcpThread::getFileSize(const string &fileName)
{
	ifstream file(fileName, ifstream::binary);
	file.seekg(0, file.end);
	int size = file.tellg();
	file.close();
	return size;
}


int TcpThread::getTextFileCount(const string &fileName)
{
	ifstream file(fileName);
	int count = 0;
	while (true)
	{
		int position = 0;
		while (!file.eof())
		{
			char c = file.get();
			if (c != EOF)
			{
				position++;
				if (position == BUFFER_SIZE - 1)
				{
					break;
				}
			}
		}
		count++;
		if (file.eof())
		{
			break;
		}
	}
	file.close();
	return count;
}


vector<string> TcpThread::ListDirectoryContents(const char *sDir)
{
	vector<string> vec;
	WIN32_FIND_DATA fdFile;
	HANDLE hFind = NULL;

	char sPath[2048];

	//Specify a file mask. *.* = We want everything!
	sprintf_s(sPath, "%s\\*.*", sDir);

	TCHAR tchar[2048];
	for (int i = 0; i < 2048; i++)
	{
		tchar[i] = sPath[i];
	}
	if ((hFind = FindFirstFile(tchar, &fdFile)) == INVALID_HANDLE_VALUE)
	{
		printf("Path not found: [%s]\n", sDir);
		return vec;
	}

	do
	{
		//Find first file will always return "."
		//    and ".." as the first two directories.
		if (strcmp((const char*)fdFile.cFileName, ".") != 0
			&& strcmp((const char*)fdFile.cFileName, "..") != 0)
		{
			//Build up our file path using the passed in
			//  [sDir] and the file/foldername we just found:
			sprintf_s(sPath, "%s\\%s", sDir, fdFile.cFileName);

			//Is the entity a File or Folder?
			if (fdFile.dwFileAttributes &FILE_ATTRIBUTE_DIRECTORY)
			{
				//printf("Directory: %s\n", sPath);
				//ListDirectoryContents(sPath); //Recursion, I love it!
			}
			else{
				string fileName = sPath;
				int index = fileName.find_last_of("\\");
				string fileName2 = fileName.substr(index + 1, fileName.length() - index - 1);
				vec.push_back(fileName2);
				//printf("File: %s\n", fileName2.c_str());
			}
		}
	} while (FindNextFile(hFind, &fdFile)); //Find the next file.
	FindClose(hFind); //Always, Always, clean things up!
	return vec;
}


bool TcpThread::fileExist(const string &fileName)
{
	ifstream file(fileName);
	if (file.good()) 
	{
		file.close();
		return true;
	}
	else 
	{
		file.close();
		return false;
	}
}

bool TcpThread::isBinaryFile(const string &fileName)
{
	int c;
	ifstream file(fileName);
	while ((c = file.get()) != EOF && c <= 127)
		;
	if (c == EOF)
	{
		file.close();
		return false;
	}
	else
	{
		file.close();
		return true;
	}
}